package com.djhu.es.core;
/**
 * Created by zw on 2017/12/28.
 */

import com.djhu.hbase.PropertyRowMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.util.*;

/**
 * code is far away from bug with the animal protecting
 * ┏┓　　　┏┓
 * ┏┛┻━━━┛┻┓
 * ┃　　　　　　　┃
 * ┃　　　━　　　┃
 * ┃　┳┛　┗┳　┃
 * ┃　　　　　　　┃
 * ┃　　　┻　　　┃
 * ┃　　　　　　　┃
 * ┗━┓　　　┏━┛
 * 　　┃　　　┃神兽保佑
 * 　　┃　　　┃代码无BUG！
 * 　　┃　　　┗━━━┓
 * 　　┃　　　　　　　┣┓
 * 　　┃　　　　　　　┏┛
 * 　　┗┓┓┏━┳┓┏┛
 * 　　　┃┫┫　┃┫┫
 * 　　　┗┻┛　┗┻┛
 *
 * <p>
 * ---------------------------------
 */
public class PropertyBeanMapper<T> implements RowMapper<T> {

    private static final Log logger = LogFactory.getLog(PropertyRowMapper.class);
    private Class<T> mappedClass;

    private Map<String, PropertyDescriptor> mappedFields;

    private Set<String> mappedProperties;

    private boolean propertysetflag = false;

    private ConversionService conversionService = DefaultConversionService.getSharedInstance();

    public ConversionService getConversionService() {
        return this.conversionService;
    }

    public static Log getLogger() {
        return logger;
    }

    public PropertyBeanMapper(Class<T> mappedClass) {
        init(mappedClass,false);
    }

    private void init(Class<T> mappedClass,boolean propertysetflag) {
        this.propertysetflag = propertysetflag;
        this.mappedClass = mappedClass;
        this.mappedFields = new HashMap<String, PropertyDescriptor>();
        this.mappedProperties = new HashSet<String>();
        PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
        for (PropertyDescriptor pd : pds) {
            if (pd.getWriteMethod() != null) {
                this.mappedFields.put(lowerCaseName(pd.getName()), pd);
                String underscoredName = underscoreName(pd.getName());
                if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
                    this.mappedFields.put(underscoredName, pd);
                }
                this.mappedProperties.add(pd.getName());
            }
        }
    }
    protected String lowerCaseName(String name) {
        return name.toLowerCase(Locale.US);
    }

    protected String underscoreName(String name) {
        if (!StringUtils.hasLength(name)) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        result.append(lowerCaseName(name.substring(0, 1)));
        for (int i = 1; i < name.length(); i++) {
            String s = name.substring(i, i + 1);
            String slc = lowerCaseName(s);
            if (!s.equals(slc)) {
                result.append("_").append(slc);
            }
            else {
                result.append(s);
            }
        }
        return result.toString();
    }

    protected void initBeanWrapper(BeanWrapper bw) {
        ConversionService cs = getConversionService();
        if (cs != null) {
            bw.setConversionService(cs);
        }
    }
    @Override
    public T mapRow(Object object, int rowSet) throws Exception {

        T mappedObject = BeanUtils.instantiateClass(this.mappedClass);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
        initBeanWrapper(bw);

        if (object instanceof Map){
            Map map = (Map)object;
            for (Object key : map.keySet()){
                String propertykey = String.valueOf(key);
                Object value = map.get(key);

                PropertyDescriptor pd = this.mappedFields.get(key);
                if (pd != null){
                    try {
                        bw.setPropertyValue(pd.getName(), value);
                    }catch (Exception t){
                        logger.error(t);
                        if (value != null && propertysetflag ){
                            throw t;
                        }
                    }finally {
                    }
                }
            }

        }
        return mappedObject;
    }
}